package com.wiscamp.ninechapters.tags.application.services;

import com.wiscamp.ninechapters.common.GlobalState;
import com.wiscamp.ninechapters.tags.application.assemblers.EntityAssembler;
import com.wiscamp.ninechapters.tags.application.assemblers.KnowledgePointAssembler;
import com.wiscamp.ninechapters.tags.application.assemblers.TagAssembler;
import com.wiscamp.ninechapters.tags.application.dto.command.*;
import com.wiscamp.ninechapters.tags.application.dto.vo.EntityVO;
import com.wiscamp.ninechapters.tags.application.dto.vo.KnowledgePointVO;
import com.wiscamp.ninechapters.tags.application.dto.vo.TagNameVO;
import com.wiscamp.ninechapters.tags.domain.TagsErrorCodes;
import com.wiscamp.ninechapters.tags.domain.repositories.EntityRepository;
import com.wiscamp.ninechapters.tags.domain.repositories.TagRepository;
import com.wiscamp.ninechapters.tags.domain.services.EntityService;
import cube.common.core.CollectionUtils;
import cube.ddd.application.BaseAppService;
import cube.ddd.application.command.DeleteCommand;
import cube.ddd.application.command.DisableCommand;
import cube.ddd.application.command.EnableCommand;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.exception.ExceptionUtils;
import cube.ddd.utils.AppServiceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

@Service
public class EntityAppService extends BaseAppService {

    private final EntityRepository entityRepository;

    private final EntityService entityService;

    private final TagRepository tagRepository;

    public EntityAppService(EntityRepository entityRepository, EntityService entityService, TagRepository tagRepository) {
        this.entityRepository = entityRepository;
        this.entityService = entityService;
        this.tagRepository = tagRepository;
    }

    // region entity
    @HandleServiceException
    public EntityVO getEntity(long entityId) {
        return AppServiceUtils.getById(entityId, entityService::getEntity,
                EntityAssembler.Instance::toEntityVO);
    }

    @HandleServiceException
    public EntityVO getEntityByCode(String entityCode, long appId) {
        return AppServiceUtils.getByTextAndId(entityCode, appId, entityRepository::getEntityByCode,
                EntityAssembler.Instance::toEntityVO);
    }

    @HandleServiceException
    public Collection<EntityVO> getEntitiesByApplication(long appId) {
        return AppServiceUtils.getListById(appId, entityRepository::getEntitiesByApplication,
                EntityAssembler.Instance::toEntityVO);
    }

    @HandleServiceException
    public Collection<EntityVO> getEntitiesByName(String entityName, long appId) {
        return AppServiceUtils.getListByTextAndId(entityName, appId, entityRepository::getEntitiesByName,
                EntityAssembler.Instance::toEntityVO);
    }

    @HandleServiceException
    public boolean enableEntity(@NotNull EnableCommand command) {
        command.setOperatorId(getCurrentUserId());
        return AppServiceUtils.enable(command.getId(), command.getOperatorId(), entityService::enableEntity);
    }

    @HandleServiceException
    public boolean disableEntity(@NotNull DisableCommand command) {
        command.setOperatorId(getCurrentUserId());
        return AppServiceUtils.disable(command.getId(), command.getOperatorId(), entityService::disableEntity);
    }

    @HandleServiceException
    public boolean deleteEntity(@NotNull DeleteCommand command) {
        command.setOperatorId(getCurrentUserId());
        return AppServiceUtils.delete(command.getId(), command.getOperatorId(), entityService::deleteEntity);
    }

    @HandleServiceException
    public EntityVO createEntity(@NotNull CreateEntityCommand command) {
        command.setOperatorId(getCurrentUserId());
        return AppServiceUtils.createOrUpdateBy(command, command.getOperatorId(), entityService::createEntity,
                EntityAssembler.Instance::toEntityVO,
                EntityAssembler.Instance::fromCreateCommand);
    }

    @HandleServiceException
    public EntityVO updateEntity(@NotNull UpdateEntityCommand command) {
        command.setOperatorId(getCurrentUserId());
        return AppServiceUtils.createOrUpdateBy(command, command.getOperatorId(), entityService::updateEntity,
                EntityAssembler.Instance::toEntityVO,
                EntityAssembler.Instance::fromUpdateCommand);
    }
    // endregion


    // region entityTag
    @HandleServiceException
    public boolean bindTag(@NotNull BindTagCommand command) {
        command.setOperatorId(getCurrentUserId());

        var appId = command.getApplicationId();
        var entityId = entityService.getEntityIdByCode(command.getEntityCode(), appId);
        var tag = tagRepository.getTagByName(command.getGroupName(), command.getTagName(), appId);
        if (Objects.isNull(tag)) {
            ExceptionUtils.throwServiceException(TagsErrorCodes.TAG_NOT_FOUND, null);
        }
        return entityService.bindTag(appId, entityId, command.getPrimaryKeyIds(), tag.getTagId());
    }

    @HandleServiceException
    public boolean unbindTag(@NotNull UnbindTagCommand command) {
        var appId = command.getApplicationId();
        var entityId = entityService.getEntityIdByCode(command.getEntityCode(), appId);
        var tag = tagRepository.getTagByName(command.getGroupName(), command.getTagName(), appId);
        if (Objects.isNull(tag)) {
            ExceptionUtils.throwServiceException(TagsErrorCodes.TAG_NOT_FOUND, null);
        }
        Collection<Long> ids = new ArrayList<Long>();
        if (command.getPrimaryKeyIds().size() > 0) {
            ids = command.getPrimaryKeyIds().stream().toList();
        } else {
            if (command.getPrimaryKeyId() > 0) {
                ids.add(command.getPrimaryKeyId());
            }
        }

        return entityService.unbindTag(appId, entityId, ids, tag.getTagId());
    }

    @HandleServiceException
    public Collection<TagNameVO> getTagNamesByProblem(long appId, long primaryKeyId) {
        var entityId = entityService.getEntityIdByCode(GlobalState.ENTITY_PROBLEM, appId);
        var tags = entityRepository.getTagsByPrimaryKey(entityId, primaryKeyId);
        return CollectionUtils.toArrayList(tags, TagAssembler.Instance::toSimpleTagVO);
    }

    @HandleServiceException
    public Collection<TagNameVO> getTagNamesByKnowledgePoint(long appId, long primaryKeyId) {
        var entityId = entityService.getEntityIdByCode(GlobalState.ENTITY_KNOWLEDGE_POINT, appId);
        var tags = entityRepository.getTagsByPrimaryKey(entityId, primaryKeyId);
        return CollectionUtils.toArrayList(tags, TagAssembler.Instance::toSimpleTagVO);
    }

    @HandleServiceException
    public Collection<TagNameVO> getTagNamesByPrimaryKey(long appId, String entityCode, long primaryKeyId) {
        var entityId = entityService.getEntityIdByCode(entityCode, appId);
        var tags = entityRepository.getTagsByPrimaryKey(entityId, primaryKeyId);
        return CollectionUtils.toArrayList(tags, TagAssembler.Instance::toSimpleTagVO);
    }

    public Collection<Long> getPrimaryKeyIdsByTags(long appId, String entityCode, Collection<Long> tagIds) {
        var entityId = entityService.getEntityIdByCode(entityCode, appId);
        return entityRepository.getPrimaryIdsByTags(entityId, tagIds);
    }
    // endregion


    // region entityPoint
    @HandleServiceException
    public boolean bindKnowledgePoint(@NotNull BindKnowledgePointCommand command) {
        command.setOperatorId(getCurrentUserId());

        var appId = command.getApplicationId();
        var entityId = entityService.getEntityIdByCode(command.getEntityCode(), appId);
        return entityService.bindKnowledgePoint(appId, entityId, command.getPrimaryKeyId(), command.getPointId(), command.isPrimary());
    }

    @HandleServiceException
    public boolean unbindKnowledgePoint(@NotNull UnbindKnowledgePointCommand command) {
        command.setOperatorId(getCurrentUserId());

        var appId = command.getApplicationId();
        var entityId = entityService.getEntityIdByCode(command.getEntityCode(), appId);
        return entityService.unbindKnowledgePoint(appId, entityId, command.getPrimaryKeyId(), command.getPointId());
    }

    @HandleServiceException
    public Collection<KnowledgePointVO> getKnowledgePointsByProblem(long appId, long primaryKeyId) {
        var entityId = entityService.getEntityIdByCode(GlobalState.ENTITY_PROBLEM, appId);
        var points = entityRepository.getKnowledgePointsByPrimaryKey(entityId, primaryKeyId);
        return CollectionUtils.toArrayList(points, KnowledgePointAssembler.Instance::toKnowledgePointVO);
    }

    @HandleServiceException
    public Collection<KnowledgePointVO> getPrimaryKnowledgePointsByProblem(long appId, long primaryKeyId) {
        var entityId = entityService.getEntityIdByCode(GlobalState.ENTITY_PROBLEM, appId);
        var points = entityRepository.getKnowledgePointsByPrimaryKey(entityId, primaryKeyId, true);
        return CollectionUtils.toArrayList(points, KnowledgePointAssembler.Instance::toKnowledgePointVO);
    }

    @HandleServiceException
    public Collection<KnowledgePointVO> getKnowledgePointsByPrimaryKey(long appId, String entityCode, long entityKeyId) {
        var entityId = entityService.getEntityIdByCode(entityCode, appId);
        var points = entityRepository.getKnowledgePointsByPrimaryKey(entityId, entityKeyId);
        return CollectionUtils.toArrayList(points, KnowledgePointAssembler.Instance::toKnowledgePointVO);
    }

    @HandleServiceException
    public Collection<KnowledgePointVO> getMinorKnowledgePointsByPrimaryKey(long appId, long primaryKeyId) {
        var entityId = entityService.getEntityIdByCode(GlobalState.ENTITY_PROBLEM, appId);
        var points = entityRepository.getKnowledgePointsByPrimaryKey(entityId, primaryKeyId, false);
        return CollectionUtils.toArrayList(points, KnowledgePointAssembler.Instance::toKnowledgePointVO);
    }

    @HandleServiceException
    public Collection<KnowledgePointVO> getKnowledgePointsByPrimaryKeyIds(long appId, String entityCode, Collection<Long> entityKeyIds) {
        var entityId = entityService.getEntityIdByCode(entityCode, appId);
        var points = entityRepository.getKnowledgePointsByPrimaryKeys(entityId, entityKeyIds);
        return CollectionUtils.toArrayList(points, KnowledgePointAssembler.Instance::toKnowledgePointVO);
    }

    @HandleServiceException
    public Collection<KnowledgePointVO> getKnowledgePointsByPrimaryKey(long appId, String entityCode, long entityKeyId, boolean isPrimary) {
        var entityId = entityService.getEntityIdByCode(entityCode, appId);
        var points = entityRepository.getKnowledgePointsByPrimaryKey(entityId, entityKeyId, isPrimary);
        return CollectionUtils.toArrayList(points, KnowledgePointAssembler.Instance::toKnowledgePointVO);
    }

    public Collection<Long> getPrimaryKeyIdsByKnowledgePoints(long appId, String entityCode, Collection<Long> pointIds) {
        var entityId = entityService.getEntityIdByCode(entityCode, appId);
        return entityRepository.getPrimaryKeyIdsByKnowledgePoints(entityId, pointIds);
    }

    @HandleServiceException
    public long countPrimaryKeyIdsByPoint(@Min(0) long appId, String entityCode, @Min(0) long pointId) {
        var entityId = entityService.getEntityIdByCode(entityCode, appId);
        return entityService.countPrimaryKeyIdsByPoint(entityId, pointId);
    }
    // endregion
}
